# @Time : 2020/10/8 17:30
# @Author : Fioman 
# @Phone : 13149920693


import time
import cv2 as cv
import imutils
import numpy as np
from .settings import *

def image_show(name, image, time, showState=False):
    """
    显示图像,调试的时候使用,根据一定的比率进行缩放. 调试的时候可以将if False,改成if True
    :param name:
    :param image:
    :param time:
    :param showState:
    :return:
    """
    if True:
        rate = 6
        width, height = int(image.shape[1] / rate), int(image.shape[0] / rate)
        showImage = cv.resize(image, (width, height), interpolation=cv.INTER_AREA)
        cv.namedWindow(name, cv.WINDOW_AUTOSIZE)
        cv.imshow(name, showImage)
        cv.waitKey(0)


def get_width_and_height(fileName):
    """
    根据文件名获取宽高,主要是用来测试算法时候使用.文件名必须是保存的固定格式的文件名.包括w_和h_,并且w_和h_的单位是像素,一般都会超过1000个像素
    :param fileName: 类似188--2020-09-21_16-45-46--w_1989--h_1745.bmp 这种图像文件
    :return: 提取出来 w: 1989, h: 1745
    """
    if fileName[-4:] != ".bmp" or "w_" not in fileName or "h_" not in fileName:
        return None, None
    widthIndex = fileName.index("w_")
    heightIndex = fileName.index("h_")
    width = int(fileName[widthIndex + 2:widthIndex + 6])
    height = int(fileName[heightIndex + 2:heightIndex + 6])
    return width, height


def find_all_board(image, width, height, filterThres, areaRate):
    """
    寻找整个物料区域的算法
    :param image: 原始图像
    :param width: 宽度
    :param height: 高度
    :param filterThres: 阈值分割的阈值
    :param areaRate: 轮廓过滤条件,如果小于面积的这个比率就认为不是轮廓
    :return:返回处理之后的整个图,还有就是物料区域整个板的box位置信息
    """
    # 1. 先对原图进行阈值分割
    T, board = cv.threshold(image, filterThres, 255, cv.THRESH_BINARY)
    image_show("FindAllBoardThres", board, 1, True)
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    # 2. 然后连接所有找到的满足要求的区域,通过白线连接起来,形成整个物料框
    singleBoardArea = width * height
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > areaRate * singleBoardArea]
    if len(largeContours) >= 2:
        centerPoints = [(int(cv.minAreaRect(contour)[0][0]), int(cv.minAreaRect(contour)[0][1])) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        # 3. 重新再找一次轮廓
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntFinalFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntFinalFinded = largeContours[0]

    box = cv.minAreaRect(cntFinalFinded)
    (cx, cy), (w, h), angle = box
    if angle > 70:
        angle = 90 - angle
        box = (cx, cy), (h, w), angle
    elif angle < -70:
        angle = 90 + angle
        box = (cx, cy), (h, w), angle

    rectPoints = np.int0(cv.boxPoints(box))
    boxMask = np.zeros(image.shape, dtype=np.uint8)
    cv.drawContours(boxMask, [rectPoints], -1, 255, -1)
    imageBlackAround = cv.bitwise_and(image, boxMask)
    return imageBlackAround, box


def get_rect_points_clockwise(rectPoints):
    """
    获取一个矩形框的四个顶点,按照从左上角开始,顺时针方向的顺序
    :param rectPoints: 获取到的四个顶点坐标
    :return:
    """
    # 1.先进行坐标之和排序,左上角和右下角先确定
    rectPointsSorted = sorted(rectPoints, key=np.sum)
    leftTop = rectPointsSorted[0]
    rightBottom = rectPointsSorted[3]

    # 2.然后另外两个框,对比x的值.x比较大的是rightTop
    if rectPointsSorted[1][0] > rectPointsSorted[2][0]:
        rightTop = rectPointsSorted[1]
        leftBottom = rectPointsSorted[2]
    else:
        rightTop = rectPointsSorted[2]
        leftBottom = rectPointsSorted[1]
    return leftTop, rightTop, rightBottom, leftBottom


def distance(p1, p2):
    """
    计算平面上两个点之间的欧式几何距离
    :param p1:
    :param p2:
    :return:
    """
    assert len(p1) == 2
    assert len(p2) == 2
    dis = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
    return round(dis, 3)


def get_two_points_middle(p1, p2):
    """
    计算平面上两个点的中点
    :param p1:
    :param p2:
    :return:
    """
    assert len(p1) == 2
    assert len(p2) == 2
    middleX = (p1[0] + p2[0]) / 2
    middleY = (p1[1] + p2[1]) / 2
    return round(middleX, 2), round(middleY, 2)


def check_board_size(cnt, width, height, calcThresBig, calcThresSmall):
    """
    对找到的内板进行尺寸验证,如果满足条件返回"ok",不满足返回尺寸错误,或者是直接是叠板情况
    :param cnt:
    :param width:
    :param height:
    :param calcThresBig:
    :param calcThresSmall:
    :return:
    """
    res = {"result": "fail", "box": None, "info": "", "calcSize": None, "calcError": None}
    try:
        widthMax = width + calcThresBig * MM2PIX
        heightMax = height + calcThresBig * MM2PIX
        widthMin = width - calcThresSmall * MM2PIX
        heightMin = height - calcThresSmall * MM2PIX

        box = cv.minAreaRect(cnt)
        rectPoints = np.int0(cv.boxPoints(box))

        # 获取四个顶点坐标,从左上角顺时针方向排列
        leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectPoints)
        widthFinded = distance(leftTop, rightTop)
        heightFinded = distance(leftTop, leftBottom)
        res["calcSize"] = (widthFinded, heightFinded)
        if widthFinded < widthMin:
            res["result"] = "small"
            res["info"] = "宽度识别过小,识别宽度:{:.2f},工单宽度:{:.2f},阈值:{:.2f}".format(widthFinded // MM2PIX, width // MM2PIX, widthMin // MM2PIX)
            return res
        elif widthFinded > widthMax:
            res["result"] = "big"
            res["info"] = "宽度识别过大,识别宽度:{:.2f},工单宽度:{:.2f},阈值:{:.2f}".format(widthFinded // MM2PIX, width // MM2PIX, widthMax // MM2PIX)
            return res
        elif heightFinded < heightMin:
            res["result"] = "small"
            res["info"] = "高度识别过小,识别高度:{:.2f},工单高度:{:.2f},阈值:{:.2f}".format(heightFinded // MM2PIX, height // MM2PIX, heightMin // MM2PIX)
            return res
        elif heightFinded > heightMax:
            # 判断是否找到了图像的边界如果是,就证明是没有中缝的情况,这个时候就要通过画椭圆的方式搞定.
            res["result"] = "big"
            res["info"] = "高度识别过大,识别高度:{:.2f},工单高度:{:.2f},阈值:{:.2f}".format(heightFinded // MM2PIX, height // MM2PIX, heightMax // MM2PIX)
            return res
        else:
            res["result"] = "ok"
            res["box"] = [leftTop, rightTop, rightBottom, leftBottom]
            res["info"] = "识别成功"
            res["calcError"] = (abs(widthFinded - width) + abs(heightFinded - height)) // MM2PIX
            return res
    except Exception as e:
        res["result"] = "error"
        res["info"] = str(e)
        return res


def check_single_board(floodFill, width, height, calcThresBig, calcThresSmall, boardColor, boardPos):
    """
    检测找到的内板是否满足要求
    :param floodFill: 最后处理的单板图
    :param width: 内板宽
    :param height: 内板高
    :param calcThresBig:偏大阈值
    :param calcThresSmall: 偏小阈值
    :param boardColor: 整个物料区域的彩色图,用于画框的时候使用
    :return: 返回res,包括识别的信息
    """
    useImg = boardColor.copy()
    boardArea = width * height
    res = {"result": "fail", "box": None, "info": "", "calcSize": None, "calcError": None}
    cnts = cv.findContours(floodFill.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    s = 0
    w = 0
    h = 0
    try:
        _, (w, h), _ = cv.minAreaRect(cntsSorted[0])
        s = w * h
    except Exception as e:
        pass
    if len(cnts) == 0:
        res["result"] = "fail"
        res["info"] = "找内板的时候没有找到任何轮廓"
        return res
    elif 1.3 * boardArea > s > 0.8 * boardArea:
        rectBox = cv.minAreaRect(cntsSorted[0])
        rectBoxPoints = np.int0(cv.boxPoints(rectBox))
        cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
        image_show("InnerBoardFinded", useImg, 0, True)
        leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectBoxPoints)

        if abs(get_two_points_middle(leftBottom, rightBottom)[1] - floodFill.shape[0]) < 10 * MM2PIX and boardPos == "up" and abs(
                get_two_points_middle(leftTop, rightTop)[1]) >= 10 * MM2PIX:  # 没有中缝的情况
            topMid = np.add(leftTop, rightTop) / 2
            topX = topMid[0]
            topY = topMid[1] + height
            ellipseR = int(height * 0.3)
            ellipseLong = int(width * 1.1)
            center = (int(topX), int(topY - ellipseR))
            ellipseImg = floodFill.copy()
            cv.ellipse(ellipseImg, center, (ellipseLong, ellipseR), 0, 0, 180, 0, 3)
            image_show("Ellipse", ellipseImg, 1, True)

            # 再找一次轮廓
            cnts = cv.findContours(ellipseImg, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
            cnts = imutils.grab_contours(cnts)
            cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            useImg = boardColor.copy()
            cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImg, 0, True)
        if abs(get_two_points_middle(leftTop, rightTop)[1]) < 10 * MM2PIX and boardPos == "down" and abs(
                get_two_points_middle(leftBottom, rightBottom)[1] - floodFill.shape[0]) > 10 * MM2PIX:  # 下框没有中缝的情况
            bottomMid = np.add(leftBottom, rightBottom) / 2
            bottomX = bottomMid[0]
            bottomY = bottomMid[1] - height
            ellipseR = int(height * 0.3)
            ellipseLong = int(width * 1.1)
            center = (int(bottomX), int(bottomY + ellipseR))
            ellipseImg = floodFill.copy()
            cv.ellipse(ellipseImg, center, (ellipseLong, ellipseR), 0, 180, 360, 0, 3)
            image_show("Ellipse", ellipseImg, 1, True)

            # 再找一次轮廓
            cnts = cv.findContours(ellipseImg, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
            cnts = imutils.grab_contours(cnts)
            cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            useImg = boardColor.copy()
            cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImg, 0, True)
        res = check_board_size(cntsSorted[0], width, height, calcThresBig, calcThresSmall)
        return res
    elif boardArea >= 1.2 * boardArea:
        # 找到的板过大,可能是找不到中缝引起的,整个
        res["result"] = "big"
        res["info"] = "识别的面积过大"
        res["calcSize"] = (floodFill.shape[1], floodFill.shape[0])
        res["calcError"] = int((abs(floodFill.shape[1] - w) + abs(floodFill.shape[0] - h)) // MM2PIX)
        return res


def get_single_inner_board(board, jointLine, width, height, calcThresBig, calcThresSmall, boardPos):
    """
    获取内板的顶点坐标,在旋转之后的TotalBoard上的
    :param board: 传过来的包括一个内板的原始图
    :param jointLine:传过来的包括一个内板的原始图的拼接处
    :param width:内板宽
    :param height:内板高
    :param calcThresBig:偏大阈值
    :param calcThresSmall:偏小阈值
    :param boardPos:板的位置,例如两拼板,就是上和下
    :return:返回值,是否识别成功,识别的信息,上框顶点坐标,下框顶点坐标,识别的内板尺寸{"upSize":(w,h),"downSize":(w,h)},识别的尺寸误差:{"upError":(wE,hE),
            "downError":(wE,hE)}
    """
    rectWidth = max(int(width * 0.33), int(150 * MM2PIX))
    rectHeight = int(200 * MM2PIX)
    boardColor = cv.cvtColor(board, cv.COLOR_GRAY2BGR)

    blurred = cv.GaussianBlur(board,(0,0),2)
    scharrX = cv.Scharr(blurred, cv.CV_64F, 1, 0)
    scharrY = cv.Scharr(blurred, cv.CV_64F, 0, 1)
    scharrX = cv.convertScaleAbs(scharrX)
    scharrY = cv.convertScaleAbs(scharrY)
    scharrXY = cv.addWeighted(scharrX, 0.5, scharrY, 0.5, 0)
    image_show("ScharrXY", scharrXY, 0, True)

    # 处理梯度的图像
    img32 = np.float32(scharrXY)
    square1 = cv.blur(np.multiply(img32, img32), (9, 9))
    square2 = np.multiply(cv.blur(img32, (9, 9)), cv.blur(img32, (9, 9)))
    squareDif32 = np.subtract(square1, square2)
    squareDif = squareDif32.copy()
    np.clip(squareDif32, 0, 255, out=squareDif)
    squareDif = squareDif.astype("uint8")
    image_show("squareDif", squareDif, 1, True)

    # 先使用我的算法去计算,计算的思路就是循环.使用一个offset还有thresInit的值
    thresInit = 180
    thresOffset = 0
    loopInit = 3
    loopOffset = 0
    kernel = [(11, 11), (17, 17), (9, 9)]
    kernelIndex = 0
    resList = []  # 将识别的结果全部保存起来,如果去评估识别的结果是否正确.它们填的尺寸非常重要.如果实验的次数过多,对它们填的尺寸要求比较高.
    for calcTime in range(5):
        contourImg = squareDif.copy()
        kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, kernel[kernelIndex])
        for i in range(3):
            contourImg = cv.dilate(contourImg, kernelUsed)
        for i in range(3):
            contourImg = cv.erode(contourImg, kernelUsed)
        image_show("ScharrXYDilateErode", contourImg, 0, True)
        thresUsed = contourImg[rectHeight:int(contourImg.shape[0] - rectHeight), rectWidth:contourImg.shape[1] - rectWidth]
        # _, contourImg = cv.threshold(contourImg, np.mean(thresUsed) + thresInit + thresOffset, 255, cv.THRESH_BINARY)
        _,contourImg = cv.threshold(contourImg,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
        contourImg = cv.subtract(contourImg, jointLine)

        # 进行泛红填充

        
        mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
        cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
        floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
        floodFill[contourImg == 150] = 255
        image_show("FloodFill", floodFill, 0, True)

        # 进行腐蚀膨胀去掉噪点
        for i in range(15):
            floodFill = cv.erode(floodFill, kernelUsed)
        for i in range(15):
            floodFill = cv.dilate(floodFill, kernelUsed)

        image_show("FloodFillErodeDilate", floodFill, 0, True)

        # 去掉两个角,要分是上框还是下框. 不去除中缝的角,只去除1,3刀的角
        if boardPos == "up":
            cv.rectangle(floodFill, (0, 0), (rectWidth, rectHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - rectWidth, rectHeight), 0, -1)
        elif boardPos == "down":
            cv.rectangle(floodFill, (0, floodFill.shape[0]), (rectWidth, floodFill.shape[0] - rectHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], floodFill.shape[0]),
                         (floodFill.shape[1] - rectWidth, floodFill.shape[0] - rectHeight), 0, -1)
        image_show("MoveAngle", floodFill, 0, True)
        res = check_single_board(floodFill, width, height, calcThresBig, calcThresSmall, boardColor, boardPos)
        print("第{}次识别结果,识别阈值:({},{}),使用内核:{},腐蚀膨胀次数:{},识别结果:{},结果信息:{}".format(
            calcTime + 1, np.mean(thresUsed), thresInit + thresOffset, kernel[kernelIndex], loopInit + loopOffset, res["result"], res["info"]))
        if res["result"] == "ok":
            return "ok", res["info"], res["box"], res["calcSize"], res["calcError"]
        elif res["result"] == "big":
            resList.append(res)
            thresOffset -= 1
            continue
        elif res["result"] == "small":
            resList.append(res)
            thresOffset += 1
            continue
    else:
        thresInit = 150
        thresOffset = 0
        loopInit = 3
        loopOffset = 0
        # 如果上面的方式没有识别出来.就不用梯度的方法,就用另外一种不需要高斯模糊的方法
        # 处理梯度的图像
        blurred = cv.GaussianBlur(board, (0, 0), 10)
        img32 = np.float32(blurred)
        square1 = cv.blur(np.multiply(img32, img32), (17, 17))
        square2 = np.multiply(cv.blur(img32, (17, 17)), cv.blur(img32, (17, 17)))
        squareDif32 = np.subtract(square1, square2)
        squareDif = squareDif32.copy()
        np.clip(squareDif32, 0, 255, out=squareDif)
        squareDif = squareDif.astype("uint8")
        image_show("squareDif", squareDif, 1, True)
        for calcTime in range(5):
            contourImg = squareDif.copy()
            kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, kernel[kernelIndex])
            for i in range(loopInit + loopOffset):
                contourImg = cv.dilate(contourImg, kernelUsed)
            for i in range(loopInit + loopOffset):
                contourImg = cv.erode(contourImg, kernelUsed)
            image_show("ScharrXYDilateErode", contourImg, 0, True)
            thresUsed = contourImg[rectHeight:int(contourImg.shape[0] - rectHeight), rectWidth:contourImg.shape[1] - rectWidth]
            _, contourImg = cv.threshold(contourImg, np.mean(thresUsed) + thresInit + thresOffset, 255, cv.THRESH_BINARY)
            contourImg = cv.subtract(contourImg, jointLine)
            # 进行泛红填充
            mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
            cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
            floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
            floodFill[contourImg == 150] = 255
            image_show("FloodFill", floodFill, 0, True)

            # 进行腐蚀膨胀去掉噪点
            for i in range(loopInit + loopOffset):
                floodFill = cv.erode(floodFill, kernelUsed)
            for i in range(loopInit + loopOffset):
                floodFill = cv.dilate(floodFill, kernelUsed)

            image_show("FloodFillErodeDilate", floodFill, 0, True)

            # 去掉两个角,要分是上框还是下框. 不去除中缝的角,只去除1,3刀的角
            if boardPos == "up":
                cv.rectangle(floodFill, (0, 0), (rectWidth, rectHeight), 0, -1)
                cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - rectWidth, rectHeight), 0, -1)
            elif boardPos == "down":
                cv.rectangle(floodFill, (0, floodFill.shape[0]), (rectWidth, floodFill.shape[0] - rectHeight), 0, -1)
                cv.rectangle(floodFill, (floodFill.shape[1], floodFill.shape[0]),
                             (floodFill.shape[1] - rectWidth, floodFill.shape[0] - rectHeight), 0, -1)
            image_show("MoveAngle", floodFill, 0, True)
            res = check_single_board(floodFill, width, height, calcThresBig, calcThresSmall, boardColor, boardPos)
            print("第{}次识别结果,识别阈值:({},{}),使用内核:{},腐蚀膨胀次数:{},识别结果:{},结果信息:{}".format(
                calcTime + 1, np.mean(thresUsed), thresOffset, kernel[kernelIndex], loopInit + loopOffset, res["result"], res["info"]))
            if res["result"] == "ok":
                return "ok", res["info"], res["box"], res["calcSize"], res["calcError"]
            elif res["result"] == "big":
                resList.append(res)
                thresOffset -= 1
                kernelIndex = 1
                loopOffset += 2
                continue
            elif res["result"] == "small":
                resList.append(res)
                thresOffset += 1
                kernelIndex = 2
                loopOffset -= 2
                continue


def get_two_boxes(image, width, height, calcThresBig, calcThresSmall, noCut):
    """
    获取图像的两个内板框的坐标.在物料区域上的顶点坐标
    :param image: 图像的灰度图
    :param width: 内板的尺寸宽度,单位像素
    :param height: 内板的尺寸高度,单位像素
    :param calcThresBig: 识别的结果较大阈值,单位mm
    :param calcThresSmal: 识别的结果较小阈值,单位mm
    :param noCut:是否四边都不切
    :return:返回值,是否识别成功,识别的信息,框的顶点坐标,识别的内板尺寸{"upSize":(w,h),"downSize":(w,h)},识别的尺寸误差:{"upError":(wE,hE),
            "downError":(wE,hE)},totalBoard整个物料区域
    """
    if image is None:
        return False, "图像异常,请重拍", None, None, None, None, None
    if len(image.shape) == 3:
        image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    image_show("Before", image, 1, True)
    # 分成如下几部去找
    # 1> 寻找整个板的物料区域
    imageGray, totalBoardBox = find_all_board(image, width, height, np.mean(image[:150, :1100]) + 15, 0.1)
    # 2> 判断找到的物料区域是否满足要求.物料区域的高度不能太大,如果大于一定的值,就证明找错了,整个值需要在settings里面设置
    (cx, cy), (w, h), angle = totalBoardBox
    if h > int(TOTAL_BOARD_HEIGHT * MM2PIX):
        return "fail", "寻找得到的物料框高度是:{},超过物料框高度阈值:{}".format(h, int(TOTAL_BOARD_HEIGHT * MM2PIX))
    widthHalf = int(w / 2)
    heightHalf = int(h / 2)
    # 3> 将找到的物料区域从图上扣出来,并且旋转成一个正矩形,左上角坐标是(0,0),右下角坐标是(w,h)
    yStart = max(int(cy - heightHalf), 0)
    yEnd = min(int(cy + heightHalf), image.shape[0])
    xStart = max(int(cx - widthHalf), 0)
    xEnd = min(int(cx + widthHalf), image.shape[1])
    M = cv.getRotationMatrix2D((cx, cy), angle, 1)
    totalBoard = cv.warpAffine(imageGray, M, (image.shape[1], image.shape[0]))[yStart:yEnd, xStart:xEnd]
    jointLineMask = np.zeros(image.shape, dtype=np.uint8)
    jointLineMask[:, 1106 - 10:1106 + 10] = 255
    jointLineMaskRotated = cv.warpAffine(jointLineMask, M, (image.shape[1], image.shape[0]))[yStart:yEnd, xStart:xEnd]
    image_show("TotalBoard", totalBoard, 0, True)
    totalBoardColor = cv.cvtColor(totalBoard, cv.COLOR_GRAY2BGR)

    # 4> 根据是几拼板进行分框处理
    if jointNumber == "2":
        upDownSlice = int(20 * MM2PIX)
        upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
        downBoard = totalBoard.copy()[int(heightHalf - upDownSlice):, :]
        upJointLine = jointLineMaskRotated.copy()[:int(heightHalf + upDownSlice), :]
        downJointLine = jointLineMaskRotated.copy()[int(heightHalf - upDownSlice):, :]
        upRes, upInfo, upBox, upCalcSize, upError = get_single_inner_board(upBoard, upJointLine, width, height, calcThresBig, calcThresSmall, "up")
        downRes, downInfo, downBox, downCalcSize, downError = get_single_inner_board(downBoard, downJointLine, width, height,
                                                                                     calcThresBig, calcThresSmall, "down")
        if upRes == "ok" and downRes == "ok":
            downBoxPoints = [(p[0], p[1] + heightHalf - upDownSlice) for p in downBox]
            cv.drawContours(totalBoardColor, [np.array(upBox), np.array(downBoxPoints)], -1, (0, 255, 0), 2)
            image_show("TwoBoxes", totalBoardColor, 0, True)
            return "ok", upInfo + downInfo, upBox, downBox, (upCalcSize, downCalcSize), (upError, downError), totalBoard
        else:
            return "sizeDif", upInfo + downInfo, upBox, downBox, (upCalcSize, downCalcSize), (upError, downError), totalBoard


if __name__ == '__main__':
    filePath = r"D:\jianding_raw\new"
    MM2PIX = 3.2038
    mtx = [[0, 1, 0], [1, 0, 0]]  # 模拟一个矩阵
    calcThresBig = 15  # 如果算大了的误差可以是15
    calcThresSmall = 10    # 如果算小的误差只能是7个毫米
    remainEdge = [1, 1, 1, 1]
    jointNumber = "2"
    topRemain, leftRemain, bottomRemain, rightRemain = [int(x * MM2PIX) for x in remainEdge]
    noCut = False if any(remainEdge) else True  # 是否四边都不切
    nowTime = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
    keepDir = filePath
    if os.path.isfile(filePath):
        keepDir, _ = os.path.split(filePath)
    # 保存成功结果的实际路径
    # 保存失败图片的实际路径
    keepResPath = os.path.join(keepDir, nowTime.split("_")[0] + "_ok")
    keepFailPath = os.path.join(keepDir, nowTime.split("_")[0] + "_fail")
    # 如果两个目录都不存在就创建
    if not os.path.exists(keepResPath):
        os.mkdir(keepResPath)
    if not os.path.exists(keepFailPath):
        os.mkdir(keepFailPath)

    fileNames = []  # 保存要去识别的所有的板
    if os.path.isfile(filePath):
        filePath, fileName = os.path.split(filePath)
        fileNames.append(fileName)
    else:
        for root, dirs, files in os.walk(filePath):
            if root != filePath:  # 过滤掉不是要处理的图像的目录
                continue
            for file in files:
                fileNames.append(file)

    # 开始遍历整个图像目录
    for index, fileName in enumerate(fileNames):
        filePathReal = os.path.join(filePath, fileName)
        widthTest, heightTest = get_width_and_height(fileName)
        if widthTest is None or heightTest is None:
            print("*" * 10 + " 第{} 张图 ({}) 图像名称错误,已经跳过".format(index + 1, fileName) + "*" * 10)

        print("*" * 10 + " 第{} 张图 ({})".format(index + 1, fileName) + "*" * 10)
        img = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)
        resSizeDifList = []
        totalBoardTest = None

        # 开始调用算法去测试
        calcStart = time.time()  # 测试开始时间
        if jointNumber == "2":
            calcRes, calcInfo, upBox, downBox, boardSize, boardError, totalBoardTest = get_two_boxes(img, widthTest, heightTest, calcThresBig,
                                                                                                     calcThresSmall, noCut)
            if calcRes == "ok":
                print("识别成功")
            elif calcRes == "sizeDif":
                print("识别尺寸错误" + calcInfo)
                cv.imwrite(os.path.join(keepFailPath, fileName), img)
                continue
            else:
                print("识别失败" + calcInfo)
                cv.imwrite(os.path.join(keepFailPath, fileName), img)
                continue
